﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using UnityEditor;
using UnityEngine;

public static class XCEditorUtils
{
    public static Regex CreateExclusiveRegex(ArrayList excludes)
    {
        return CreateExclusiveRegex(excludes.ToArray().Select(e => e.ToString()).ToArray());
    }

    public static Regex CreateExclusiveRegex(params string[] excludes)
    {
        Regex regex = null;

        if (excludes != null && excludes.Length > 0)
        {
            System.Text.StringBuilder sbPattern = new System.Text.StringBuilder();

            foreach (string s in excludes)
            {
                sbPattern.Append(s).Append('|');
            }

            sbPattern.Remove(sbPattern.Length - 1, 1);

            regex = new Regex(sbPattern.ToString());
        }

        return regex;
    }

    public static void CopyFiles(string srcRoot, 
        string dstRoot, 
        Hashtable copyFiles, 
        ArrayList copyFileExcludes)
    {
        Dictionary<string, string> copyFilesDict = HashtableToDictionary<string, string>(copyFiles);
        Regex excludeRegex = CreateExclusiveRegex(copyFileExcludes);

        CopyFiles(srcRoot, dstRoot, copyFilesDict, excludeRegex);
    }

    public static void CopyFiles(string srcRoot, 
                                 string dstRoot, 
                                 Dictionary<string, string> copyFilesDict,
                                 Regex excludeRegex)
    {
        foreach (KeyValuePair<string, string> copyFilesEntry in copyFilesDict)
        {
            string srcFilePath = copyFilesEntry.Key;
            string dstFilePath = copyFilesEntry.Value;

            string srcAbsPath = CombinePaths(srcRoot, srcFilePath);
            string dstAbsPath = CombinePaths(dstRoot, dstFilePath);

            CopyFiles(srcAbsPath, dstAbsPath, excludeRegex);
        }
    }

    public static void CopyFiles(string srcAbsPath, string dstAbsPath, Regex excludeRegex)
    {
        Debug.Log("'" + srcAbsPath + "' -> '" + dstAbsPath + "'");

        if (excludeRegex != null && excludeRegex.IsMatch(srcAbsPath))
        {
            Debug.Log("Excluded file/folder '" + srcAbsPath + "'");
            return;
        }

        if (File.Exists(srcAbsPath))
        {
            string dstAbsPathDirName = Path.GetDirectoryName(dstAbsPath);

            if (!Directory.Exists(dstAbsPathDirName))
            {
                Directory.CreateDirectory(dstAbsPathDirName);
            }

            File.Copy(srcAbsPath, dstAbsPath, true);
        }
        else if (Directory.Exists(srcAbsPath))
        {
            DirectoryInfo srcDirInfo = new DirectoryInfo(srcAbsPath);
            DirectoryInfo dstDirInfo;

            if (Directory.Exists(dstAbsPath))
            {
                dstDirInfo = new DirectoryInfo(dstAbsPath);
            }
            else
            {
                dstDirInfo = Directory.CreateDirectory(dstAbsPath);
            }

            foreach (FileInfo fileInfo in srcDirInfo.GetFiles())
            {
                CopyFiles(fileInfo.FullName, Path.Combine(dstDirInfo.FullName, fileInfo.Name), excludeRegex);
            }

            foreach (DirectoryInfo dirInfo in srcDirInfo.GetDirectories())
            {
                CopyFiles(dirInfo.FullName, Path.Combine(dstDirInfo.FullName, dirInfo.Name), excludeRegex);
            }
        }
        else
        {
            Debug.Log("Skipping non-existing file/folder '" + srcAbsPath + "'");
        }
    }

    // http://stackoverflow.com/questions/20618809/hashtable-to-dictionary
    public static Dictionary<K,V> HashtableToDictionary<K,V> (Hashtable table)
    {
        Dictionary<K,V> dict = new Dictionary<K,V>();
        foreach(DictionaryEntry kvp in table)
            dict.Add((K)kvp.Key, (V)kvp.Value);
        return dict;
    }

    public static string CombinePaths(params string[] paths)
    {
        if (paths.Length == 0)
        {
            return "";
        }

        List<string> segments = new List<string>();

        char[] separators = new char[] { '/' };

        for (int i = 0; i < paths.Length; i++)
        {
            string path = paths[i];

            string[] currSegments = path.Split(separators, StringSplitOptions.RemoveEmptyEntries);

            for (int j = 0; j < currSegments.Length; j++)
            {
                string currSegment = currSegments[j];

                switch (currSegment)
                {
                    case ".":
                        break;

                    case "..":
                        if (segments.Count > 0)
                        {
                            segments.RemoveAt(segments.Count - 1);
                        }
                        else
                        {
                            segments.Add("..");
                        }
                        break;

                    default:
                        segments.Add(currSegment);
                        break;
                }
            }
        }

        StringBuilder sb = new StringBuilder();

        if (paths[0].StartsWith("./"))
        {
            sb.Append("./");
        }
        else if (paths[0].StartsWith("/"))
        {
            sb.Append("/");
        }

        for (int i = 0; i < segments.Count; i++)
        {
            sb.Append(segments[i]).Append('/');
        }

        string s = sb.ToString();

        if (!paths[paths.Length - 1].EndsWith("/"))
        {
            s = s.TrimEnd('/');  
        }

        return s;
    }

    public static string AssetPathToFullPath(string assetPath)
    {
        assetPath = assetPath.Replace("\\", "/");

        int idxAssetsDir = assetPath.IndexOf("Assets/");

        if (idxAssetsDir != 0)
        {
            throw new ArgumentException("Not a valid asset path: " + assetPath, "assetPath");
        }

        if (assetPath.Length == "Assets/".Length)
        {
            assetPath = "";
        }
        else
        {
            assetPath = assetPath.Substring("Assets/".Length);
        }

        return CombinePaths(Application.dataPath, assetPath);
    }

    public static string FullPathToAssetPath(string fullPath)
    {
        fullPath = fullPath.Replace('\\', '/');

        int idxAssetPath = fullPath.IndexOf(Application.dataPath);

        if (idxAssetPath != 0)
        {
            throw new ArgumentException("Not a valid full path: " + fullPath, "fullPath");
        }

        string assetPath = "Assets/" + fullPath.Substring(Application.dataPath.Length).TrimStart('/');

        return assetPath;
    }

    public static string ChangeExtension(string filename, string newExtension)
    {
        int idxExtension = filename.LastIndexOf(".");

        if (idxExtension == filename.Length - 1)
        {
            throw new ArgumentException("The input filename does not contain a valid file extension.", "filename");
        }

        if (idxExtension == 0)
        {
            throw new ArgumentException("The input filename does not contain a file name.", "filename");
        }

        if (filename[idxExtension - 1] == '.' || filename[idxExtension + 1] == '.')
        {
            throw new ArgumentException("The input filename does not contain a valid file extension.", "filename");
        }

        return filename.Substring(0, idxExtension) + newExtension;
    }
}
